import * as THREE from "three";
import { shelfManager } from "./ShelfManager";
import { mergeGeometries } from "three/examples/jsm/utils/BufferGeometryUtils";
import { Cargo } from "./Cargo";
import { Circle } from "./Circle";

const part1Material = new THREE.MeshPhysicalMaterial( { color: 0xcccccc } );
const part2Material = new THREE.MeshPhysicalMaterial();

let $id = 0;

export class Shelf {

    object: THREE.Object3D;

    cargoPoints: CargoPoint[];
    cargos: Cargo[];

    id: number;
    name: string;

    cargoCount: number;
    maxCargoCount: number;

    get manager () {
        return shelfManager;
    }

    get isFull () {
        return this.cargoCount === this.maxCargoCount;
    }

    get isEmpty () {
        return this.cargoCount === 0;
    }

    constructor ( name: string = "" ) {

        this.object = new THREE.Object3D();
        this.cargoPoints = [];
        this.cargos = [];
        this.id = $id++;
        this.name = name;

        this.cargoCount = 0;
        this.maxCargoCount = 0;

        shelfManager.addShelf( this );

    }

    /**
     * 
     * @param length 货架长度
     * @param width 货架宽度
     * @param layerNumber 货架层数
     * @param layerHeight 货架每层高度
     * @param firstLayerHeight 货架第一层距离地面的高度
     * @param pointNumber 货架每层几个置物点（一个置物点可以堆放一个 x*x*x 的货品）
     */
    init ( length: number = 1.5, width: number = 0.5, layerNumber: number = 2, layerHeight: number = 0.5, firstLayerHeight: number = 0.5, pointNumber: number = 2 ) {

        this.maxCargoCount = layerNumber * pointNumber;

        for ( let i = 0; i < layerNumber + 1; i++ ) {

            const part1 = this.createPart1( length, width, 0.01 );
            const part1_y = firstLayerHeight + layerHeight * i;
            part1.position.y = part1_y;
            this.object.add( part1 );


            if ( i < layerNumber ) {

                const l = length / pointNumber;

                for ( let j = 0; j < pointNumber; j++ ) {

                    const cargoPoint = new CargoPoint( this, i, j );

                    cargoPoint.object.position.set( -length / 2 + l * j + l / 2, part1_y, 0 );

                    this.cargoPoints[ i * pointNumber + j ] = cargoPoint;

                }
            }

        }

        shelfManager.updateTree();

        // 创建货架的4个支架脚
        const totalHeight = layerHeight * layerNumber + firstLayerHeight;
        const part2Width = 0.03, part2WidthHalf = 0.015;
        const part2 = this.createPart2( length, width, totalHeight, part2Width, part2WidthHalf );

        this.object.add( part2 );

    }

    /** 设置货架位置 */
    setPosition ( x: number, y: number, z: number ) {
        this.object.position.set( x, y, z );
    }

    /** 向货架添加货物,如果货架已满，则多余的不添加 */
    addCargo ( cargo: Cargo, cargoPoint: CargoPoint = undefined ): boolean {

        if ( this.isFull ) {
            return false;
        }

        if ( cargoPoint !== undefined ) {

            cargoPoint.addCargo( cargo );

            cargo.shelf = this;
            cargo.cargoPoint = cargoPoint;

            this.cargoCount++;

            shelfManager.updateTree();

            this.cargos.push( cargo );

            return true;
        } else {

            // 获取货架所有的可以添加货物的点位
            const cargoPoints = this.cargoPoints;

            for ( let i = 0; i < cargoPoints.length; i++ ) {

                const cargoPoint = cargoPoints[ i ];

                // 如果点位为空，说明可以向该点位放置货物
                if ( cargoPoint.isEmpty ) {

                    cargoPoint.addCargo( cargo );

                    cargo.shelf = this;
                    cargo.cargoPoint = cargoPoint;

                    this.cargoCount++;

                    shelfManager.updateTree();

                    this.cargos.push( cargo );

                    return true;

                }

            }

        }

        return false;

    }

    /** 从货架删除货物 */
    removeCargo ( cargo: Cargo ): boolean {

        if ( this.isEmpty || !cargo.cargoPoint ) {
            return false;
        }

        const cargoPoint = cargo.cargoPoint;
        cargoPoint.removeCargo();

        cargo.shelf = null;
        cargo.cargoPoint = null;

        this.cargoCount--;

        shelfManager.updateTree();

        const cargos = this.cargos;
        for ( let i = 0; i < cargos.length; i++ ) {
            if ( cargos[ i ] === cargo ) {
                cargos.splice( i, 1 );
            }
        }

        return true;

    }

    getCargoPoint ( vec3: THREE.Vector3 ) {
        return shelfManager.getCargoPoint( vec3 );
    }

    update ( delta: number ) {
        const cargoPoints = this.cargoPoints;

        for ( let i = 0; i < cargoPoints.length; i++ ) {

            const cargoPoint = cargoPoints[ i ];

            cargoPoint.update( delta );

        }
    }

    /** 创建货架平面 */
    private createPart1 ( length: number, width: number, depth = 0.01 ) {

        const geometry = new THREE.BoxGeometry( length, depth, width );
        const mesh = new THREE.Mesh( geometry, part1Material );
        return mesh;

    }

    /** 创建货架支架 */
    private createPart2 ( length: number, width: number, height: number, part2width: number, part2widthHalf: number ) {

        const geometry = new THREE.BoxGeometry( part2width, height, part2width );
        const matrix = new THREE.Matrix4();

        const part2_1 = geometry.clone();
        part2_1.applyMatrix4( matrix.makeTranslation( new THREE.Vector3( length / 2 - part2widthHalf, height / 2, width / 2 - part2widthHalf ) ) );

        const part2_2 = geometry.clone();
        part2_2.applyMatrix4( matrix.makeTranslation( new THREE.Vector3( length / 2 - part2widthHalf, height / 2, part2widthHalf - width / 2 ) ) );

        const part2_3 = geometry.clone();
        part2_3.applyMatrix4( matrix.makeTranslation( new THREE.Vector3( part2widthHalf - length / 2, height / 2, width / 2 - part2widthHalf ) ) );

        const part2_4 = geometry.clone();
        part2_4.applyMatrix4( matrix.makeTranslation( new THREE.Vector3( part2widthHalf - length / 2, height / 2, part2widthHalf - width / 2 ) ) );

        const part2Geometry = mergeGeometries( [ part2_1, part2_2, part2_3, part2_4 ] );
        const part2 = new THREE.Mesh( part2Geometry, part2Material );

        return part2;
    }

}


class ShelfLayer {

    constructor () {

    }

    init () {

    }
}

/** 储物点 */
export class CargoPoint {

    isEmpty: boolean;

    object: THREE.Object3D;

    /** 储物点上的货物 */
    cargo: Cargo | undefined;

    /** 储物点所在的货架 */
    shelf: Shelf;

    /** 储物点所在货架的层数 */
    layer: number;

    /** 储物点在每层的索引 */
    index: number;

    /** 储物点所在货架的ID */
    shelfId: number;

    /** */
    circle: Circle;

    position: THREE.Vector3 = new THREE.Vector3();

    constructor ( shelf: Shelf, layer: number, index: number ) {

        this.isEmpty = true;

        this.object = new THREE.Object3D();

        this.cargo = undefined;

        this.shelf = shelf;

        shelf.object.add( this.object );
        this.object.getWorldPosition( this.position );

        this.circle = new Circle( 0.1, 24 );
        this.object.add( this.circle );

        this.layer = layer;
        this.index = index;
        this.shelfId = shelf.id;

    }

    addCargo ( cargo: Cargo ) {

        this.isEmpty = false;
        this.cargo = cargo;
        cargo.object.position.set( 0, 0, 0 );
        this.object.add( cargo.object );
        this.circle.visible = false;

    }

    removeCargo () {

        const cargo = this.cargo;

        this.object.remove( cargo.object );
        this.isEmpty = true;
        this.cargo = null;
        this.circle.visible = true;

        cargo.cargoPoint = null;

    }

    update ( delta: number ) {
        this.circle.update( delta );
    }

}